using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using litgui = UnityEditor.Rendering.Universal.ShaderGUI.LitGUI;

namespace CNGraphi.U6URP.Editor
{
    static class Property
    {
        public static readonly string SpecularWorkflowMode = "_WorkflowMode";
        public static readonly string SurfaceType = "_Surface";
        public static readonly string BlendMode = "_Blend";
        public static readonly string AlphaClip = "_AlphaClip";
        public static readonly string AlphaToMask = "_AlphaToMask";
        public static readonly string SrcBlend = "_SrcBlend";
        public static readonly string DstBlend = "_DstBlend";
        public static readonly string SrcBlendAlpha = "_SrcBlendAlpha";
        public static readonly string DstBlendAlpha = "_DstBlendAlpha";
        public static readonly string BlendModePreserveSpecular = "_BlendModePreserveSpecular";
        public static readonly string ZWrite = "_ZWrite";
        public static readonly string CullMode = "_Cull";
        public static readonly string CastShadows = "_CastShadows";
        public static readonly string ReceiveShadows = "_ReceiveShadows";
        public static readonly string QueueOffset = "_QueueOffset";

        // 仅针对ShaderGraph属性
        public static readonly string ZTest = "_ZTest";
        public static readonly string ZWriteControl = "_ZWriteControl";
        public static readonly string QueueControl = "_QueueControl";

        // Global Illumination 要求特定地命名某些属性
        public static readonly string EmissionMap = "_EmissionMap";
        public static readonly string EmissionColor = "_EmissionColor";
    }

    enum MaterialUpdateType
    {
        /// <summary>
        /// 创建新材质
        /// </summary>
        CreatedNewMaterial,
        /// <summary>
        /// 重新分配着色器
        /// </summary>
        ChangedAssignedShader,
        /// <summary>
        /// 修改着色器
        /// </summary>
        ModifiedShader,
        /// <summary>
        /// 修改材质
        /// </summary>
        ModifiedMaterial
    }


    /// <summary>
    /// 适用于基于 ShaderGraph (Lit、Unlit) 类型的着色器
    /// <para>作者：CN.Graphi</para>
    /// </summary>
    public abstract class ShaderGraphGUI : BaseShaderGUI
    {
        public MaterialProperty workflowMode;
        protected MaterialProperty[] properties;



        /// <summary>
        /// 获取所有着色属性
        /// <para>这里包含 ShaderGraph 内置默认的属性</para>
        /// </summary>
        /// <param name="properties"></param>
        public override void FindProperties(MaterialProperty[] properties)
        {
            this.properties = properties;

            var material = materialEditor?.target as Material;
            if (material == null)
                return;

            base.FindProperties(properties);
            workflowMode = FindProperty(Property.SpecularWorkflowMode, properties, false);
        }


        /// <summary>
        /// 更新材质
        /// </summary>
        /// <param name="material"></param>
        /// <param name="updateType"></param>
        void UpdateMaterial(Material material, MaterialUpdateType updateType)
        {
            if (updateType == MaterialUpdateType.CreatedNewMaterial)
                material.globalIlluminationFlags = MaterialGlobalIlluminationFlags.BakedEmissive;

            // 内置Lit的ShaderGUI操作
            //bool automaticRenderQueue = GetAutomaticQueueControlSetting(material);
            //BaseShaderGUI.UpdateMaterialSurfaceOptions(material, automaticRenderQueue);
            //litgui.SetupSpecularWorkflowKeyword(material, out bool isSpecularWorkflow);

            // 新的方式（由反射机制执行。因为调用的都是unity内部internal修饰的关键函数）
            Type t = typeof(BaseShaderGUI);
            bool automaticRenderQueue = ReflectUtils.CallbackMethod<bool>(t, "GetAutomaticQueueControlSetting", material);
            ReflectUtils.CallMethod(t, "UpdateMaterialSurfaceOptions", material, automaticRenderQueue);
            SetupSpecularWorkflowKeyword(material, out bool isSpecularWorkflow);
        }


        /// <summary>
        /// 设置高光工作流的关键字开启和关闭
        /// </summary>
        /// <param name="material"></param>
        /// <param name="isSpecularWorkflow"></param>
        void SetupSpecularWorkflowKeyword(Material material, out bool isSpecularWorkflow)
        {
            isSpecularWorkflow = false; // 默认金属工作流
            if (material.HasProperty(Property.SpecularWorkflowMode))
                isSpecularWorkflow = ((litgui.WorkflowMode)material.GetFloat(Property.SpecularWorkflowMode)) == litgui.WorkflowMode.Specular;
            CoreUtils.SetKeyword(material, "_SPECULAR_SETUP", isSpecularWorkflow);
        }

        /// <summary>
        /// 材质发生任何改变时调用
        /// </summary>
        /// <param name="material"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public override void ValidateMaterial(Material material)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            UpdateMaterial(material, MaterialUpdateType.ModifiedMaterial);
        }


        /// <summary>
        /// 绘制材质表面类型选项卡
        /// </summary>
        /// <param name="material"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public override void DrawSurfaceOptions(Material material)
        {
            if (material == null)
                throw new ArgumentNullException("material");

            EditorGUIUtility.labelWidth = 0f;

            if (workflowMode != null)
            {
                // TODO: 
                //      将原有的rgba改为rgb解析。unity 使用 rgba 是为了兼容金属工作流和高光工作流，高光工作流mask贴图的rgba分配为，rgb为高光颜色，a为平滑度。
                //      但在实际工作中，常使用的是金属工作流。由于兼容了高光工作流，rgb被颜色使用，但金属工作流中，urp管线对rgb都分配了金属度，实际上浪费了gb两
                //      个通道。因此，在这里将内置的mask、occ、detail三张贴图进行优化合并。同时标准物理光照PBR Lit着色器只支持金属工作流，屏蔽高光工作流。
                EditorGUI.BeginDisabledGroup(true);
                DoPopup(litgui.Styles.workflowModeText, workflowMode, Enum.GetNames(typeof(litgui.WorkflowMode)));
                EditorGUI.EndDisabledGroup();
                Gui.Tip("For detailed instructions, please refer to the script <i><color=#ff9900><ShaderGraphiGUI> line: 151</color></i>");
            }
            base.DrawSurfaceOptions(material);
        }



        /// <summary>
        /// 绘制高级选项卡
        /// </summary>
        /// <param name="material"></param>
        public override void DrawAdvancedOptions(Material material)
        {
            DoPopup(Styles.queueControl, queueControlProp, Styles.queueControlNames);
            if (material.HasProperty(Property.QueueControl) && material.GetFloat(Property.QueueControl) == (float)QueueControl.UserOverride)
                materialEditor.RenderQueueField();
            base.DrawAdvancedOptions(material);

            materialEditor.DoubleSidedGIField();
            materialEditor.LightmapEmissionFlagsProperty(0, enabled: true, ignoreEmissionColor: true);
        }



        /// <summary>
        /// 材质中所有表面输入的参数绘制
        /// </summary>
        /// <param name="material"></param>
        public override void DrawSurfaceInputs(Material material)
        {
            // 内置 Lit 调用的是 shadergraph.dll 程序集中 internal 修饰的类及函数
            //DrawShaderGraphProperties(material, properties);

            // 子类实现各自的表面输入属性的绘制
            EditorGUILayout.Space(20);
            DrawCustomSurfaceInput(material);
            EditorGUILayout.Space(10);
        }


        /// <summary>
        /// 绘制自定义表面输入的属性
        /// <para>子类重写</para>
        /// </summary>
        /// <param name="material">材质</param>
        protected virtual void DrawCustomSurfaceInput(Material material) { }



        /// <summary>
        /// 绘制属性
        /// </summary>
        /// <param name="propname">属性名称</param>
        /// <param name="displayname">显示名称</param>
        protected void ShowProperty(string propname, string displayname = "")
        {
            if (string.IsNullOrEmpty(propname)) { return; }
            ShowProperty(FindProp(propname), displayname);

            //float h = materialEditor.GetPropertyHeight(prop, prop.displayName);
            //Rect r = EditorGUILayout.GetControlRect(true, h, EditorStyles.layerMaskField);
            //materialEditor.ShaderProperty(r, prop, prop.displayName);
        }
        protected void ShowProperty(MaterialProperty prop, string displayname = "")
        {
            if (prop == null) { return; }
            materialEditor.ShaderProperty(prop, string.IsNullOrEmpty(displayname) ? prop.displayName : displayname);
        }


        /// <summary>
        /// 获取属性在 Editor 检视面板中可显示的范围
        /// </summary>
        /// <param name="prop"></param>
        /// <returns></returns>
        protected Rect GetRect(MaterialProperty prop)
        {
            return EditorGUILayout.GetControlRect(true, MaterialEditor.GetDefaultPropertyHeight(prop));
        }


        /// <summary>
        /// 快速获取属性
        /// </summary>
        /// <param name="pname">属性引用名</param>
        /// <returns>未找到，则会返回null，并抛出异常</returns>
        protected MaterialProperty FindProp(string pname)
        {
            if (string.IsNullOrEmpty(pname)) { return null; }
            return FindProperty(pname, properties);
        }


        /// <summary>
        /// 绘制 Vector2 类型的参数
        /// </summary>
        /// <param name="propname"></param>
        /// <param name="displayname"></param>
        protected void DrawVector2Property(string propname, string displayname = "")
        {
            DrawVector2Property(FindProp(propname), displayname);
        }
        /// <summary>
        /// 绘制 Vector2 类型的参数
        /// </summary>
        /// <param name="property"></param>
        /// <param name="displayname"></param>
        protected void DrawVector2Property(MaterialProperty property, string displayname = "")
        {
            EditorGUI.BeginChangeCheck();
            EditorGUI.showMixedValue = property.hasMixedValue;
            Vector2 newValue = EditorGUI.Vector2Field(GetRect(property), string.IsNullOrEmpty(displayname) ? property.displayName : displayname, new Vector2(property.vectorValue.x, property.vectorValue.y));
            EditorGUI.showMixedValue = false;
            if (EditorGUI.EndChangeCheck())
            {
                property.vectorValue = newValue;
            }
        }


        /// <summary>
        /// 绘制 Vector3 类型的参数
        /// </summary>
        /// <param name="propname"></param>
        /// <param name="displayname"></param>
        protected void DrawVector3Property(string propname, string displayname = "")
        {
            DrawVector3Property(FindProp(propname), displayname);
        }
        /// <summary>
        /// 绘制 Vector3 类型的参数
        /// </summary>
        /// <param name="property"></param>
        /// <param name="displayname"></param>
        protected void DrawVector3Property(MaterialProperty property, string displayname = "")
        {
            EditorGUI.BeginChangeCheck();
            EditorGUI.showMixedValue = property.hasMixedValue;
            Vector3 newValue = EditorGUI.Vector3Field(GetRect(property), string.IsNullOrEmpty(displayname) ? property.displayName : displayname, new Vector3(property.vectorValue.x, property.vectorValue.y, property.vectorValue.z));
            EditorGUI.showMixedValue = false;
            if (EditorGUI.EndChangeCheck())
            {
                property.vectorValue = newValue;
            }
        }


        /// <summary>
        /// 绘制Foldout组件
        /// </summary>
        /// <param name="foldout"></param>
        /// <param name="title"></param>
        /// <param name="action"></param>
        protected void DrawFoldout(ref bool foldout, string title, Action action)
        {
            foldout = EditorGUILayout.Foldout(foldout, title, true, new GUIStyle("OL Title") { contentOffset = new Vector2(0, 0), margin = new RectOffset(8, 0, 0, 0) });
            if (foldout)
            {
                EditorGUILayout.BeginVertical("helpbox");
                EditorGUILayout.Space(10);
                EditorGUI.indentLevel++;
                action?.Invoke();
                EditorGUI.indentLevel--;
                EditorGUILayout.Space(10);
                EditorGUILayout.EndVertical();
            }
        }
    }

}